/*******************************************************************************
  * 文件：UartComm_Dba.c
  * 作者：zyz
  * 版本：v1.0.0
  * 日期：2017-08-03
  * 说明：数据块阵列
*******************************************************************************/
/* 头文件 *********************************************************************/
#include "UartComm_Dba.h"
#include "UartComm_Driver.h"

/* 宏定义 *********************************************************************/
/* 类型定义 *******************************************************************/
/* 变量定义 *******************************************************************/
// 设备DBA信息
static UartCommDbaInfo_ts sDeviceStateDbaInfo;
static UartCommDbaInfo_ts sDeviceControlDbaInfo;

// 订阅DBA控制
static UartCommSubscribeDbaControl_ts asSubscribeCtrl[ePORT_SUM];

/* 函数声明 *******************************************************************/
// 读写数据块
static U8 UartComm_ReadWriteDataBlock(
    UartCommDataBlockInfo_ts *psDataBlockInfo,
    U8 u8DataBlockInfoNum,
    UartCommDataReadWrite_te eDataReadWrite,
    U16 u16DataAddr, U8 u8DataLen, U8 *pu8Data);
// 发送发布DBA消息
static void UartComm_SendPublishDbaMsg(U32 u32Parm);

/* 函数定义 *******************************************************************/
/*******************************************************************************
  * 函数名: UartComm_InitDba
  * 功  能：初始化
  * 参  数：无
  * 返回值：无
  * 说  明：无
*******************************************************************************/
void UartComm_InitDba(void)
{
    U8 u8Index;
    LittleEndianU16_tu uAddr;
    UartCommSubscribeDbaParm_ts *psSubscribeParm;

/* <------------------------------- 初始化设备 -----------------------------> */
    UartComm_InitDevice();

/* <------------------------------- 初始化DBA信息 --------------------------> */
    // 获取DBA信息
    sDeviceStateDbaInfo   = UartComm_GetDeviceStateDbaInfo();
    sDeviceControlDbaInfo = UartComm_GetDeviceControlDbaInfo();

/* <------------------------------- 初始化订阅参数 -------------------------> */
    for(u8Index=0; u8Index<ePORT_SUM; u8Index++)
    {
        // 停止发布DBA数据
        // 清零订阅数据块个数
        UartComm_PublishDbaData((UartCommPort_te)u8Index, FALSE);
        asSubscribeCtrl[u8Index].sSubscribeParm.u8DataBlockNum = 0;
    }

/* <------------------------------- 设置默认订阅参数 -----------------------> */
    psSubscribeParm = &(asSubscribeCtrl[ePORT_DISPLAY_BOARD].sSubscribeParm);
    // 设置订阅地址，时间，数据块个数
    psSubscribeParm->u8DeviceAddress = u8UARTCOMM_DISPLAY_BOARD_ADDR;
    psSubscribeParm->u8SubscribeTime = 1;
    psSubscribeParm->u8DataBlockNum = 2;
    // 设置订阅数据块地址和长度
    uAddr.u16Word = u16LOAD_RO_DATA_ADDR;
    psSubscribeParm->au8DataBlockParm[0] = uAddr.sByte.u8Msb;
    psSubscribeParm->au8DataBlockParm[1] = uAddr.sByte.u8Lsb;
    psSubscribeParm->au8DataBlockParm[2] = sizeof(LoadReadOnlyData_ts);
    uAddr.u16Word = u16LOAD_RW_DATA_ADDR;
    psSubscribeParm->au8DataBlockParm[3] = uAddr.sByte.u8Msb;
    psSubscribeParm->au8DataBlockParm[4] = uAddr.sByte.u8Lsb;
    psSubscribeParm->au8DataBlockParm[5] = sizeof(LoadReadWriteData_ts);
}

/*******************************************************************************
  * 函数名: UartComm_ReadWriteDataBlock
  * 功  能：读写数据块
  * 参  数：psDataBlockInfo    - 数据块信息
  *         u8DataBlockInfoNum - 数据块信息个数
  *         eDataReadWrite     - 数据读写
  *         u16DataAddr        - 数据地址
  *         u8DataLen          - 数据长度
  *         pu8Data            - 数据指针
  * 返回值：实际数据长度
  * 说  明：无
*******************************************************************************/
static U8 UartComm_ReadWriteDataBlock(
    UartCommDataBlockInfo_ts *psDataBlockInfo,
    U8 u8DataBlockInfoNum,
    UartCommDataReadWrite_te eDataReadWrite,
    U16 u16DataAddr, U8 u8DataLen, U8 *pu8Data)
{
    U8 u8Index;
    U8 u8Length = 0;

    // 查找数据块，并返回数据
    for(u8Index=0; u8Index < u8DataBlockInfoNum; u8Index++)
    {
        U16 u16Addr = psDataBlockInfo[u8Index].u16DataAddr;
        U8 u8Len    = psDataBlockInfo[u8Index].u8DataLen;
        U8 *pu8Dat  = psDataBlockInfo[u8Index].pu8Data;

        // 找到数据块
        if((u16DataAddr >= u16Addr) && (u16DataAddr < (u16Addr + u8Len)))
        {
            // 数据全在数据块内
            if((u16DataAddr + u8DataLen) <= (u16Addr + u8Len))
            {
                u8Length = u8DataLen;
            }
            // 数据不全在数据块内
            else
            {
                u8Length = u16Addr + u8Len - u16DataAddr;
            }

            // 读数据
            if(eDataReadWrite == eDATA_READ)
            {
                memcpy(pu8Data, (void*)(pu8Dat + u16DataAddr - u16Addr), u8Length);
            }
            // 写数据
            else if(eDataReadWrite == eDATA_WRITE)
            {
                memcpy((void*)(pu8Dat + u16DataAddr - u16Addr), pu8Data, u8Length);
            }
            // 按位写数据
            else if(eDataReadWrite == eDATA_BITS_WRITE)
            {
                U8 u8DataIndex = 0;
                U8 u8WriteData = 0;
                U8 *pu8WriteData = (U8*)(pu8Dat + u16DataAddr - u16Addr);

                // 遍历并写入数据
                for(u8DataIndex = 0; u8DataIndex < u8Length; u8DataIndex++)
                {
                    // 写入数据
                    pu8Data[0] &= pu8Data[1];
                    u8WriteData = pu8WriteData[0] & (~pu8Data[1]);
                    pu8WriteData[0] = u8WriteData | pu8Data[0];
                    // 更新数据指针
                    pu8Data += 2;
                    pu8WriteData++;
                }
            }
            else
            {
                u8Length = 0;
            }
        }
    }

    // 返回实际长度
    return u8Length;
}

/*******************************************************************************
  * 函数名: UartComm_ReadWriteDeviceStateDataBlock
  * 功  能：读写设备状态数据块
  * 参  数：eDataReadWrite  - 数据读写
  *         u16DataAddr     - 数据地址
  *         u8DataLen       - 数据长度
  *         pu8Data         - 数据指针
  * 返回值：实际数据长度
  * 说  明：无
*******************************************************************************/
U8 UartComm_ReadWriteDeviceStateDataBlock(
    UartCommDataReadWrite_te eDataReadWrite,
    U16 u16DataAddr, U8 u8DataLen, U8 *pu8Data)
{
    return UartComm_ReadWriteDataBlock(
               sDeviceStateDbaInfo.psDataBlockInfo,
               sDeviceStateDbaInfo.u8DataBlockInfoNum,
               eDataReadWrite,
               u16DataAddr, u8DataLen, pu8Data);
}

/*******************************************************************************
  * 函数名: UartComm_ReadWriteDeviceControlDataBlock
  * 功  能：读写设备控制数据块
  * 参  数：eDataReadWrite  - 数据读写
  *         u16DataAddr     - 数据地址
  *         u8DataLen       - 数据长度
  *         pu8Data         - 数据指针
  * 返回值：实际数据长度
  * 说  明：无
*******************************************************************************/
U8 UartComm_ReadWriteDeviceControlDataBlock(
    UartCommDataReadWrite_te eDataReadWrite,
    U16 u16DataAddr, U8 u8DataLen, U8 *pu8Data)
{
    return UartComm_ReadWriteDataBlock(
               sDeviceControlDbaInfo.psDataBlockInfo,
               sDeviceControlDbaInfo.u8DataBlockInfoNum,
               eDataReadWrite,
               u16DataAddr, u8DataLen, pu8Data);
}

/*******************************************************************************
  * 函数名: UartComm_SetSubscribeDbaParm
  * 功  能：设置订阅DBA参数
  * 参  数：ePort            - 请求端口
  *         u8Address        - 设备地址
  *         u8Time           - 订阅时间
  *         u8DataBlockNum   - 数据块个数
  *         pu8DataBlockParm - 数据块参数
  * 返回值：结果
  * 说  明：无
*******************************************************************************/
Bool UartComm_SetSubscribeDbaParm(
    UartCommPort_te ePort, U8 u8Address,
    U8 u8Time, U8 u8DataBlockNum, U8 *pu8DataBlockParm)
{
    U16 u16Len;
    Bool bResult = TRUE;
    UartCommSubscribeDbaParm_ts *psSubscribeParm;

    // 计算数据块参数长度
    u16Len = u8DataBlockNum*3;

    // 参数超长
    if(u16Len > u8UARTCOMM_SUBSCRIBE_DB_PARM_LEN)
    {
        bResult = FALSE;
    }
    // 参数未超长
    else
    {
        // 获取参数指针
        psSubscribeParm = &(asSubscribeCtrl[ePort].sSubscribeParm);

        // 保存参数
        psSubscribeParm->u8DeviceAddress = u8Address;
        psSubscribeParm->u8SubscribeTime = u8Time;
        psSubscribeParm->u8DataBlockNum  = u8DataBlockNum;
        memcpy(psSubscribeParm->au8DataBlockParm, pu8DataBlockParm, (U8)u16Len);
    }

    // 返回结果
    return bResult;
}

/*******************************************************************************
  * 函数名: UartComm_PublishDbaData
  * 功  能：发布DBA数据
  * 参  数：ePort  - 端口
  *         bState - 状态
  * 返回值：无
  * 说  明：无
*******************************************************************************/
void UartComm_PublishDbaData(UartCommPort_te ePort, Bool bState)
{
    U8 u8Time;
    UartCommSubscribeDbaControl_ts *psSubscribeCtrl;

    // 初始化参数
    psSubscribeCtrl = &asSubscribeCtrl[ePort];
    u8Time = psSubscribeCtrl->sSubscribeParm.u8SubscribeTime;

    // 发布
    if(bState)
    {
        // 订阅时间处理
        if(u8Time == 0)
        {
            u8Time = 1;
        }

        // 开启发送订阅DBA消息定时器
        OS_TimerStartEx(
            &(psSubscribeCtrl->sSubscribeTimer),
            (U16)(u8Time * 100),
            UartComm_SendPublishDbaMsg,
            (U32)ePort);
    }
    // 停止发布
    else
    {
        // 关闭发送订阅DBA消息定时器
        OS_TimerStop(&(psSubscribeCtrl->sSubscribeTimer));
    }
}

/*******************************************************************************
  * 函数名：UartComm_SendPublishDbaMsg
  * 功  能：发送发布DBA消息
  * 参  数：u32Parm - 参数，这里传递端口
  * 返回值：无
  * 说  明：无
*******************************************************************************/
static void UartComm_SendPublishDbaMsg(U32 u32Parm)
{
    U8 u8Index;
    U8 *pu8Data;
    UartCommPort_te ePort;
    UartCommSubscribeDbaParm_ts *psSubscribeParm;
    UartCommDataBlock_ts *psSubscribeDataBlock;
    UartCommDataBlock_ts *psSendDataBlock;
    UartCommDbaMsgData_ts *psSendDbaMsgData;

    // 端口判断
    if(u32Parm < (U32)ePORT_SUM)
    {
        // 获取订阅参数
        ePort = (UartCommPort_te)u32Parm;
        psSubscribeParm = &(asSubscribeCtrl[ePort].sSubscribeParm);

        // 构建发送数据包
        pu8Data = UartComm_SetupSendPacket(0,
            psSubscribeParm->u8DeviceAddress, u8UARTCOMM_CMD_PUBLISH_DBA);

        // 获取订阅数据块
        psSubscribeDataBlock = (UartCommDataBlock_ts*)psSubscribeParm->au8DataBlockParm;
        // 初始化发送数据块参数
        psSendDbaMsgData = (UartCommDbaMsgData_ts*)pu8Data;
        psSendDbaMsgData->u8DataBlockNum = psSubscribeParm->u8DataBlockNum;
        psSendDataBlock = (UartCommDataBlock_ts*)psSendDbaMsgData->au8Data;

        // 遍历数据块
        for(u8Index=0; u8Index<psSubscribeParm->u8DataBlockNum; u8Index++)
        {
            LittleEndianU16_tu uAddr;

            // 地址转换
            uAddr.sByte.u8Msb = psSubscribeDataBlock->u8DataAddrMsb;
            uAddr.sByte.u8Lsb = psSubscribeDataBlock->u8DataAddrLsb;

            // 设置发送数据块地址
            psSendDataBlock->u8DataAddrMsb = psSubscribeDataBlock->u8DataAddrMsb;
            psSendDataBlock->u8DataAddrLsb = psSubscribeDataBlock->u8DataAddrLsb;

            // 读设备状态数据块
            psSendDataBlock->u8DataLen = UartComm_ReadWriteDeviceStateDataBlock(
                eDATA_READ,
                uAddr.u16Word, psSubscribeDataBlock->u8DataLen, psSendDataBlock->au8Data);

            // 获取下一个订阅数据块
            // 更新下一个发送数据块
            psSubscribeDataBlock = (UartCommDataBlock_ts*)(psSubscribeDataBlock->au8Data);
            psSendDataBlock      = (UartCommDataBlock_ts*)(psSendDataBlock->au8Data +
                                                           psSendDataBlock->u8DataLen);
        }

        // 更新发送数据位置，发送数据包
        pu8Data = (U8*)psSendDataBlock;
        UartComm_SendPacket(ePort, pu8Data);
    }
}

/***************************** 文件结束 ***************************************/
